﻿#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)

#include <iostream>
using namespace std;

//int main()
//{
//	int a = 10;
//	cout << "hellow world" << endl;
//	cout << "a=" << a << endl;
//
//	system("pause");
//	return 0;
//}

//1.4常量
//1、宏常量
//#define day 7
//int main() 
//{
//	cout << "一周里总共有 " << day << " 天" << endl;
//	//day = 8;  //报错，宏常量不可以修改
//
//	//2、const修饰变量
//	const int month = 12;
//	cout << "一年里总共有" << month << "个月份" << endl;
//	//month = 24; //报错，常量是不可以修改的
//
//	system("pause");
//	return 0;
//}

//1.6 标识符命名规则
//作用：C++规定给标识符（变量、常量）命名时，有一套自己的规则
//  标识符不能是关键字
//  标识符只能由字母、数字、下划线组成
//  第一个字符必须为字母或下划线，不能是数字
//  标识符中字母区分大小写


/* 2 数据类型 */
//2.2 sizeof
//int main() 
//{
//	cout << "short 类型所占内存空间为： " << sizeof(short) << endl;
//	cout << "int 类型所占内存空间为： " << sizeof(int) << endl;
//	cout << "long 类型所占内存空间为： " << sizeof(long) << endl;
//	cout << "long long 类型所占内存空间为： " << sizeof(long long) << endl;
//
//	system("pause");
//	return 0;
//}
//short < int <= long <= long long

//2.3浮点型
//1. 单精度float		 7位有效数字
//2. 双精度double	15～16位有效数字
//int main() 
//{
//	float f1 = 3.14f;
//	double d1 = 3.14;
//
//	cout << f1 << endl;		//3.14
//	cout << d1 << endl;		//3.14
//	//默认情况下，输出一个小数，只显示出6位有效数字
//	cout << "float  sizeof = " << sizeof(f1) << endl;
//	cout << "double sizeof = " << sizeof(d1) << endl;
//
//	//科学计数法表示小数
//	float f2 = 3e2; // 3 * 10 ^ 2 
//	cout << "f2 = " << f2 << endl;		//300
//
//	float f3 = 3e-3;  // 3 * 0.1 ^ 3
//	cout << "f3 = " << f3 << endl;
//
//	system("pause");
//	return 0;
//}

/******************************************************************************************************************************************************/

/* 1 内存分区模型 */
//C++程序在执行时，将内存大方向划分为4个区域
//
//- 代码区：存放函数体的二进制代码，由操作系统进行管理的
//- 全局区：存放全局变量和静态变量以及字符串常量、const修饰的全局变量（全局常量）
//- 栈区：  由编译器自动分配释放, 存放函数的参数值、局部变量、const修饰的局部变量（局部常量）等
//- 堆区：  由程序员分配和释放, 若程序员不释放, 程序结束时由操作系统回收

//1.1 程序运行前
//​	在程序编译后，生成了exe可执行程序，未执行该程序前分为两个区域
//  代码区：
//​		存放 CPU 执行的机器指令
//​		代码区是共享的，共享的目的是对于频繁被执行的程序，只需要在内存中有一份代码即可
//​		代码区是只读的，使其只读的原因是防止程序意外地修改了它的指令
//
//  全局区：
//​		全局变量、静态变量、字符串常量、const修饰的全局变量（全局常量）存放在此.    const修饰的局部变量（局部常量）存放在栈区
//		该区域的数据在程序结束后由操作系统释放

////全局变量
//int g_a = 10;
//int g_b = 10;
//
////const修饰的全局变量
//const int c_g_a = 10;
//const int c_g_b = 10;
//
//int main() 
//{
//	//局部变量
//	int a = 10;
//	int b = 10;
//	//打印地址
//	cout << "局部变量a地址为： " << (int)&a << endl;
//	cout << "局部变量b地址为： " << (int)&b << endl;
//	cout << "全局变量g_a地址为： " << (int)&g_a << endl;
//	cout << "全局变量g_b地址为： " << (int)&g_b << endl;
//
//	//静态变量
//	static int s_a = 10;
//	static int s_b = 10;
//	cout << "静态变量s_a地址为： " << (int)&s_a << endl;
//	cout << "静态变量s_b地址为： " << (int)&s_b << endl;
//
//	//常量
//	cout << "字符串常量地址为： " << (int)&"hello world" << endl;
//	cout << "字符串常量地址为： " << (int)&"hello world1" << endl;
//
//	cout << "全局常量c_g_a地址为： " << (int)&c_g_a << endl;
//	cout << "全局常量c_g_b地址为： " << (int)&c_g_b << endl;
//
//	const int c_l_a = 10;		//const修饰的局部变量（局部常量）存放在栈区
//	const int c_l_b = 10;
//	cout << "局部常量c_l_a地址为： " << (int)&c_l_a << endl;		//c-const g-global全局 l-local局部
//	cout << "局部常量c_l_b地址为： " << (int)&c_l_b << endl;
//
//	system("pause");
//	return 0;
//}

//总结：
//* C++中在程序运行前分为全局区和代码区
//* 代码区特点是共享和只读
//* 全局区中存放全局变量、静态变量、常量（字符串常量和const修饰的全局变量）


//1.2 程序运行后
//	栈区：由编译器自动管理开辟和释放, 存放函数的参数值, 局部变量，const修饰的局部变量等
//​	注意事项：不要返回局部变量的地址，栈区开辟的数据在函数执行完后由编译器自动释放

//int* func()
//{
//	int a = 10;		//局部变量，存放在栈区
//	return &a;
//}
//
//int main()
//{
//	int* p = func();//此时的p是一个野指针，p指向的空间已经被释放，不存在了
//	cout << *p << endl;		//10；第一次打印10是因为编译器做了保留
//	cout << *p << endl;		//乱码
//
//	system("pause");
//	return 0;
//}

//  堆区：由程序员分配释放, 若程序员不释放, 程序结束时由操作系统回收
//  在C++中主要利用new在堆区开辟内存
//int* func()
//{
//	int* p = new int(10);		//利用new关键字可以把10开辟在堆区；此处的指针本质上就是一个局部变量，放在栈上，指针保存的数据是放在堆区的
//	return p;					//在堆区创建整型数据
//}
//
//int main() 
//{
//	int* p = func();
//	cout << *p << endl;
//	cout << *p << endl;
// 
// //利用delete释放堆区数据
//    delete p;
//    cout << *p << endl; //报错，释放的空间不可访问
// 
//	system("pause");
//	return 0;
//}

//堆区开辟数组
//int main() 
// {
//	int* arr = new int[10];		//在堆区创建有10个整形数据的数组，返回的是首元素的地址
//
//	for (int i = 0; i < 10; i++)
//	{
//		arr[i] = i;
//		cout << arr[i] << endl;
//	}
//	delete[] arr;	//释放数组 delete 后加 []
//
//	system("pause");
//	return 0;
//}

//1.3 new操作符
//C++中利用 == new == 操作符在堆区开辟数据，堆区开辟的数据，由程序员手动开辟，手动释放，释放利用操作符 == delete ==
//语法：` new 数据类型`；利用new创建的数据，会返回该数据对应的类型的指针


/* 2.引用 */
//2.1 引用的基本使用
//作用： 给变量起别名
//语法： 数据类型 &别名 = 原名
//int main()
//{
//	int a = 10;
//	int& b = a;
//
//	cout << "a = " << a << endl;	//10
//	cout << "b = " << b << endl;
//	b = 100;
//	cout << "a = " << a << endl;	//100
//	cout << "b = " << b << endl;
//
//	system("pause");
//	return 0;
//}

//2.2 引用注意事项
//* 引用必须初始化
//* 引用在初始化后，别名不可以改变，只能对应原名
//int main() {
//	int a = 10;
//	int b = 20;
//	//int &c; //错误，引用必须初始化
//	int &c = a; 
//	//int &c = b不行，一旦初始化后，就不可以更改  
//	c = b; //这是赋值操作，不是更改引用
//
//	cout << "a = " << a << endl;
//	cout << "b = " << b << endl;
//	cout << "c = " << c << endl;
//
//	system("pause");
//	return 0;
//}

//2.3 引用做函数参数
//** 作用：** 函数传参时，可以利用引用让形参改变实参（值传递不能让形参改变实参）
//** 优点：** 可以不用指针修改实参

//值传递
//void mySwap01(int a, int b) {
//	int temp = a;
//	a = b;
//	b = temp;
//}
////地址传递
//void mySwap02(int* a, int* b) {
//	int temp = *a;
//	*a = *b;
//	*b = temp;
//}
////3引用传递
//void mySwap03(int &a, int &b) {	//形参中的a是实参a的别名
//	int temp = a;
//	a = b;
//	b = temp;
//}
//
//int main() {
//
//	int a = 10;
//	int b = 20;
//
//	mySwap01(a, b);
//	cout << "a:" << a << " b:" << b << endl;	//值传递，形参是实参的一份临时拷贝，形参的变化不会改变实参
//
//	//mySwap02(&a, &b);
//	//cout << "a:" << a << " b:" << b << endl;	//地址传递，形参的变化会改变实参
//
//	mySwap03(a, b);
//	cout << "a:" << a << " b:" << b << endl;
//
//	system("pause");
//	return 0;
//}

//2.4 引用做函数返回值
//作用：引用是可以作为函数的返回值存在的
//注意：不要返回局部变量的引用
//		如果函数的返回值是引用，这个函数调用可以作为左值

//返回局部变量引用
//int& test01() {
//	int a = 10; //局部变量，存放在栈区
//	return a;	//返回的是a的引用
//}
//
////返回静态变量引用
//int& test02() {
//	static int a = 20;		//静态变量存放在全局区，该区域的数据在整个程序结束后由操作系统释放
//	return a;
//}
//
//int main() {
//
//	//不能返回局部变量的引用
//	int &ref = test01();	
//	cout << "ref = " << ref << endl;		//10
//	cout << "ref = " << ref << endl;		//乱码，内存已经被释放
//	
//	int& ref2 = test02();	//ref2就是a的别名
//	cout << "ref2 = " << ref2 << endl;
//	cout << "ref2 = " << ref2 << endl; 
//
//	test02() = 1000;		//如果函数的返回值是引用，这个函数调用可以作为左值；相当于a=1000
//
//	cout << "ref2 = " << ref2 << endl;
//	cout << "ref2 = " << ref2 << endl;
//
//	system("pause");
//	return 0;
//}

//2.5 引用的本质
//引用的本质在c++内部实现是一个指针常量.（对于指针常量，指针的指向不能被修改）

//发现是引用，转换为 int* const ref = &a;
//void func(int& ref) 
//{
//	ref = 100; // ref是引用，转换为*ref = 100
//}
//int main() 
//{
//	int a = 10;
//	int& ref = a;	 //编译器会将此代码自动转换为 int* const ref = &a; 指针常量是指针指向不可改，也说明为什么引用不可更改
//	ref = 20;		 //内部发现ref是引用，自动帮我们转换为: *ref = 20;
//
//	cout << "a:" << a << endl;
//	cout << "ref:" << ref << endl;
//
//	func(a);
//	return 0;
//}

// 2.6 常量引用
//作用：常量引用主要用来修饰形参，防止误操作
//在函数形参列表中，可以加 == const修饰形参 == ，防止形参改变实参
//引用使用的场景，通常用来修饰形参
//void showValue(const int &v)	//加上const修饰形参，防止形参改变实参
//{
//	//v += 10;
//	cout << v << endl;
//}
//int main() 
//{
//	//int& ref = 10;  引用本身需要一个合法的内存空间，因此这行错误
//	//加入const就可以了，编译器优化代码，int temp = 10; const int& ref = temp;
//	const int& ref = 10;
//
//	//ref = 100;  //加入const后不可以修改变量
//	cout << ref << endl;
//
//	//函数中利用常量引用防止误操作修改实参
//	int a = 10;
//	showValue(a);
//
//	system("pause");
//	return 0;
//}


/*3函数提高*/
//3.1 函数默认参数
//在C++中，函数的形参列表中的形参是可以有默认值的。
//语法：   返回值类型  函数名 （参数 = 默认值） 

//1. 如果某个位置参数有默认值，那么从这个位置往后，从左向右，必须都要有默认值
//int func(int a, int b = 10, int c = 10) 
//{
//	return a + b + c;
//}
////2. 如果函数声明有默认值，函数实现(函数的定义)的时候就不能有默认参数；函数的声明和函数的定义只能有一个默认参数
//int func2(int a = 10, int b = 10);
//int func2(int a, int b) 
//{
//	return a + b;
//}
//
//int main() 
//{
//	cout << "ret = " << func(20, 20) << endl;
//	cout << "ret = " << func(100) << endl;
//
//	system("pause");
//	return 0;
//}

//3.2 函数占位参数
//C++中函数的形参列表里可以有占位参数，用来做占位，调用函数时必须填补该位置
//语法：`返回值类型 函数名(数据类型) {}`
//在现阶段函数的占位参数存在意义不大，但是后面的课程中会用到该技术

//函数占位参数 ，占位参数也可以有默认参数
//void func(int a, int)		//第二个传进来的10拿不到
//{
//	cout << "this is func" << endl;
//}
//
//int main() 
//{
//	func(10, 10); //占位参数必须填补
//
//	system("pause");
//	return 0;
//}

//3.3 函数重载
//3.3.1 函数重载作用：函数名可以相同，提高复用性
// 
//函数重载满足条件： 
//* 同一个作用域下
//* 函数名称相同
//* 函数参数类型不同 或者 个数不同 或者 顺序不同

//注意:函数的返回值不可以作为函数重载的条件

//函数重载需要函数都在同一个作用域下
//void func()
//{
//	cout << "func 的调用！" << endl;
//}
//void func(int a)
//{
//	cout << "func (int a) 的调用！" << endl;
//}
//void func(double a)
//{
//	cout << "func (double a)的调用！" << endl;
//}
//void func(int a, double b)
//{
//	cout << "func (int a ,double b) 的调用！" << endl;
//}
//void func(double a, int b)
//{
//	cout << "func (double a ,int b)的调用！" << endl;
//}
//
////函数返回值不可以作为函数重载条件
////int func(double a, int b)
////{
////	cout << "func (double a ,int b)的调用！" << endl;
////}
//
//int main() 
// {
//	func();
//	func(10);
//	func(3.14);
//	func(10, 3.14);
//	func(3.14, 10);
//
//	system("pause");
//	return 0;
//}

//3.3.2 函数重载注意事项
//1、引用作为重载条件
//void func(int& a)		//int &a = 10 不合法
//{
//	cout << "func (int &a) 调用 " << endl;
//}
//void func(const int& a)//const int &a = 10 合法
//{
//	cout << "func (const int &a) 调用 " << endl;
//}
//
////2、函数重载碰到函数默认参数
//void func2(int a, int b = 10)
//{
//	cout << "func2(int a, int b = 10) 调用" << endl;
//}
//void func2(int a)
//{
//	cout << "func2(int a) 调用" << endl;
//}
//
//int main() 
//{
//	int a = 10;
//	func(a); //调用参数无const的func
//	func(10);//调用有const
//	//func2(10); //10都可以传到两个func2函数；碰到默认参数产生歧义，需要避免不写默认参数
//
//	system("pause");
//	return 0;
//}